﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Text.RegularExpressions;

namespace SqliteDriver
{
	internal class SqliteSchemaParser
	{
		//enum Fragment {first, field, constraint};
		//internal static DbView.FieldInfo [] Parse(String schemaDef)
		//{
		//    List<DbView.FieldInfo> fi = new List<DbView.FieldInfo>();
			
		//    String tableDef = ExtractFieldInfoFromSchema(schemaDef);
		//    String[] segments = tableDef.Replace("\r","").Replace("\n","").Replace("\t", " ").Split(',');
			
		//    foreach (string rawSegment in segments)
		//    {
		//        String segment = rawSegment.Trim();
		//        Fragment f = ParseFragment(segment);
		//        switch (f)
		//        {
		//            case Fragment.first:
		//                fi.Add(MakeField(RemoveHeader(segment)));
		//                break;
		//            case Fragment.field:
		//                fi.Add(MakeField(segment));
		//                break;
		//            case Fragment.constraint:
		//                ProcessConstraint(segment, fi);
		//                break;
		//        }
		//    }
		//    return fi.ToArray();
		//}


		//private static string ExtractFieldInfoFromSchema(string schemaDef)
		//{
		//    // Extract the fields from "Create table xxxx( *this is what is returned * )"
		//    int iPos1 = schemaDef.IndexOf("(");
		//    int iPos2 = schemaDef.LastIndexOf(")");
		//    // bad string
		//    if (iPos1 == -1 || iPos2 == -1 || iPos2 < iPos1)
		//        return "";
		//    return schemaDef.Substring(iPos1 + 1, iPos2 - iPos1 - 1);
		//}

		//private static DbView.FieldInfo MakeField(string segment)
		//{
		//    // won't cope with [this field]
		//    // column_name [type] constraint
		//    String fullPattern = @"^\s*(.*?)\s+(.*?)\s+(.*?)$";
		//    Regex full = new Regex(fullPattern);
		//    // column_name [type]
		//    String normalPattern = @"^\s*(.*?)\s+(.*?)$";
		//    Regex norm = new Regex(normalPattern);
		//    // column_name
		//    String briefPattern = @"^\s*(.*?)\s*$";
		//    Regex brief = new Regex(briefPattern);

		//    Regex longest = (full.IsMatch(segment)) ? full :
		//                                                    (norm.IsMatch(segment)) ? norm : 
		//                                                    brief;
		//    Match m = longest.Match(segment);
			
		//    String rname = m.Groups[1].Value;
		//    rname = CheckRemoveBrackets(rname);
		//    String rtype = "C";  // default is char
		//    String rtypeRaw = "";  // default is nothing
		//    int rlength = 0;
		//    bool isPkey = false;
		//    bool isAuto = false;
		//    bool isFK = false;
		//    bool allowNull = true;
		//    if (m.Groups.Count > 2)
		//    {
		//        rtypeRaw = m.Groups[2].Value;
		//        rlength = GetTypeLength(rtypeRaw);
		//        rtype = MapType(rtypeRaw);
		//        // deal with primary key, constrainsts etc.
		//        if (m.Groups.Count > 3)
		//        {
		//            String extra = m.Groups[3].Value.ToString().Trim();
		//            if (extra.ToUpper().IndexOf("PRIMARY") >= 0)
		//            {
		//                isPkey = true;
		//                if (extra.ToUpper().IndexOf("AUTOINCREMENT") != -1)
		//                    isAuto = true;
		//            }
		//            if (extra.ToUpper().IndexOf("REFERENCES") >= 0)
		//                isFK = true;
		//            int nn1 = extra.ToUpper().IndexOf("NOT");
		//            int nn2 = extra.ToUpper().IndexOf("NULL");
		//            if (nn1 >= 0 && nn2 > nn1)
		//                allowNull = false;

		//        }
		//    }
		//    DbView.FieldInfo fi = new DbView.FieldInfo(rname, rtype, rlength);
		//    fi.SqlType = rtypeRaw;
		//    fi.IsPKey = isPkey;
		//    fi.IsAuto = isAuto;
		//    fi.IsFKey = isFK;
		//    fi.AllowsNulls = allowNull && !(fi.IsPKey); 
		//    // 
		//    return fi;
		//}

		//private static string MapType(string rtype)
		//{
		//    if (rtype.ToUpper().IndexOf("INT") != -1) return "INT";
		//    if (rtype.ToUpper().IndexOf("CHAR") != -1) return "CHAR";
		//    if (rtype.ToUpper().IndexOf("TEXT") != -1) return "CHAR";
		//    return "CHAR";
		//}

		//private static int GetTypeLength(string rtype)
		//{
		//    Regex seek = new Regex(@"^(.*?)\((\d+)");
		//    Match m = seek.Match(rtype);
		//    if (!m.Success)
		//        return 0; 
		//    return Convert.ToInt32(m.Groups[2].Value);
		//}

		//private static void ProcessConstraint(string segment, List<DbView.FieldInfo> fi)
		//{
		//    String fieldsPattern = @"^(.*?)\((.*?)\).*?$";
		//    Regex fields = new Regex(fieldsPattern);
		//    Match m = fields.Match(segment);
		//    if (m.Success)
		//    {
		//        string [] fieldList = m.Groups[2].Value.Split(',');
		//        foreach (string field in fieldList)
		//        {
		//            foreach (DbView.FieldInfo tgtfi in fi)
		//            {
		//                if (NameMatch(tgtfi.Name, field))
		//                {
		//                    if (segment.ToUpper().IndexOf("PRIMARY") >= 0)
		//                        tgtfi.IsPKey = true;
		//                    if (segment.ToUpper().IndexOf("FOREIGN KEY") >= 0)
		//                        tgtfi.IsFKey = true;
		//                }
		//            }
		//        }
		//    }
		//}

		private static bool NameMatch(string nameA, string nameB)
		{
			string n1 = CheckRemoveBrackets(nameA).ToUpper();
			string n2 = CheckRemoveBrackets(nameB).ToUpper();
			return (n1 == n2);
		}

		private static string CheckRemoveBrackets(string name)
		{
			if (name[0] == '[' && name[name.Length - 1] == ']')
				name = name.Substring(1, name.Length - 2);
			return name;
		}

		//private static string RemoveHeader(string segment)
		//{
		//    int bPos = segment.IndexOf("(");
		//    return segment.Substring(bPos+1);
		//}

		//private static Fragment ParseFragment(string segment)
		//{
		//    String seek = segment.TrimStart().ToUpper();
		//    if (seek.IndexOf("CREATE") != -1 && seek.IndexOf("TABLE") != -1)
		//        return Fragment.first;
		//    if (seek.IndexOf("CONSTRAINT") == 0 ||
		//        seek.IndexOf("PRIMARY KEY") == 0 ||
		//        seek.IndexOf("UNIQUE") == 0 ||
		//        seek.IndexOf("CHECK") == 0 ||
		//        seek.IndexOf("FOREIGN KEY") == 0
		//       )
		//        return Fragment.constraint;
		//    return Fragment.field;
		//}

		////////////////////////////////////////////////////////////////////////////////////
		// This bit adds the indexes given a datatable with row 0 = table definition 1 - n = index def
		// and that the table name is column 2 of the first row.
		//public static void AddIndexInfo_old(DataTable infoTable, DbView.FieldInfo[] fi)
		//{
		//    String tableName = infoTable.Rows[0][1].ToString();
		//    String indexPattern = String.Format(@"^\s*CREATE.*?INDEX.*?(\[|\s){0}(\]|\s*)\((.*?)\).*?$", tableName);
		//    Regex index = new Regex(indexPattern, RegexOptions.IgnoreCase);
		//    foreach (DataRow dr in infoTable.Rows)
		//    {
		//        Match m = index.Match(dr[0].ToString());
		//        if (m.Success)
		//        {
		//            string [] fieldList = m.Groups[3].Value.Split(',');
		//            foreach (string field in fieldList)
		//            {
		//                foreach (DbView.FieldInfo tgtfi in fi)
		//                {
		//                    if (NameMatch(tgtfi.Name, field))
		//                        tgtfi.IsIndexed = true;
		//                }
		//            }
		//        }
		//    }
		//}


		internal static DbView.FieldInfo MakeFieldInfo(DataRow dr)
		{
			string name = dr[1].ToString();
			string type = dr[2].ToString();
			int len = 0;
			int dp = 0;
			string fieldLenPattern = @"\((\s*\d+\s*)\)";
			string numericPattern = @"\(((\s*\d+\s*),(\s*\d+\s*))\)";
			Regex fieldLength = new Regex(fieldLenPattern);
			Regex numeric = new Regex(numericPattern);
			Regex extractor = numeric.IsMatch(type) ? numeric : fieldLength;
			Match m = extractor.Match(type);
			if (m.Success)
			{
				if (extractor == numeric)
				{
					len = Convert.ToInt32(m.Groups[2].Value);
					dp = Convert.ToInt32(m.Groups[3].Value);
				}
				else
					len = Convert.ToInt32(m.Groups[1].Value);
				type = type.Replace(m.Groups[0].Value, "");
			}
			// extract length
			DbView.FieldInfo fi = new DbView.FieldInfo(name, type, len);
			fi.Dp = dp;
			// other stuff from other 
			// 3 = not null
			fi.AllowsNulls = (dr[3].ToString() != "1");
			// 5 = pk
			fi.IsPKey = (dr[5].ToString() == "1");
			if (fi.IsPKey)
				fi.AllowsNulls = false;
			return fi;
		}

		internal static void AddFkInfo(DataTable dataTable, DbView.FieldInfo[] fields)
		{
			// 0,1, 2=pk table 3=fk field 4=pk
			int fkFieldCol = 3;
			// throw new NotImplementedException();
			foreach (DataRow fkRow in dataTable.Rows)
			{
				String fkField = fkRow[fkFieldCol].ToString();
				foreach (DbView.FieldInfo fi in fields)
				{
					if (NameMatch(fi.Name, fkField))
						fi.IsFKey = true;
				}
			}
		}
		internal static void AddIndexInfo(DataTable dataTable, DbView.FieldInfo[] fields)
		{
			// 0 is index field
			int idxFieldCol = 0;
			// throw new NotImplementedException();
			foreach (DataRow fkRow in dataTable.Rows)
			{
				String indexField = fkRow[idxFieldCol].ToString();
				foreach (DbView.FieldInfo fi in fields)
				{
					if (NameMatch(fi.Name, indexField))
						fi.IsIndexed = true;
				}
			}

		}

		internal static void AddMiscSchemaInfo(DataTable dataTable, DbView.FieldInfo[] fields)
		{
			string schema = dataTable.Rows[0][0].ToString();
			if (schema.ToUpper().IndexOf("AUTOINCREMENT") > 0)
			{
				foreach (DbView.FieldInfo fi in fields)
				{
					if (fi.IsPKey)
						fi.IsAuto = true;
				}
			}
		}
	}
}
